package loppy.dynamics 
{
  import loppy.collision.Collision;
  import loppy.loppy_internal;
  import loppy.utils.InList;
  import loppy.utils.InListIterator;
  import loppy.utils.MathUtils;
  
  internal class ContactManager 
  {
    use namespace loppy_internal;
    
    loppy_internal var contacts:InList = new InList();
    
    public function add(contact:Contact):void
    {
      var edge1 :ContactEdge = ContactEdge.pool.get(); //from body 1 to body 2
      var edge2 :ContactEdge = ContactEdge.pool.get(); //from body 2 to body 1
      var shape1:Shape       = contact.shape1;
      var shape2:Shape       = contact.shape2;
      var body1 :Body        = shape1.parent;
      var body2 :Body        = shape2.parent;
      
      //check if the contact already exists
      for (var edgeIter:InListIterator = body1.contactEdges.begin(); !edgeIter.isEnd(); edgeIter.next())
      {
        var edge:ContactEdge = edgeIter.data();
        var existingContact:Contact = edge.contact;
        if (existingContact.shape1 == shape1 && existingContact.shape2 == shape2) return;
        if (existingContact.shape1 == shape2 && existingContact.shape2 == shape1) return;
      }
      
      contact.type = contact.shape1.type | contact.shape2.type;
      
      edge1.contact = contact;
      edge1.other   = body2;
      body1.contactEdges.add(edge1);
      contact.edge1 = edge1;
      
      edge2.contact = contact;
      edge2.other   = body1;
      body2.contactEdges.add(edge2);
      contact.edge2 = edge2;
      
      contacts.add(contact);
    }
    
    public function remove(contact:Contact):void
    {
      var edge1 :ContactEdge = contact.edge1;
      var edge2 :ContactEdge = contact.edge2;
      var shape1:Shape       = contact.shape1;
      var shape2:Shape       = contact.shape2;
      var body1 :Body        = shape1.parent;
      var body2 :Body        = shape2.parent;
      
      body1.contactEdges.remove(edge1);
      body2.contactEdges.remove(edge2);
      
      contacts.remove(contact);
      Contact.pool.put(contact.dispose());
      ContactEdge.pool.put(edge1.dispose());
      ContactEdge.pool.put(edge2.dispose());
    }
    
    public function update():void
    {
      var contactIter:InListIterator = contacts.begin();
      while (!contactIter.isEnd())
      {
        var contact:Contact = contactIter.data();
        
        //check if the contact is still touching
        contact.update();
        
        if (!contact.isTouching)
        {
          //not touching, check proxy collision
          if (!Collision.collideProxies(contact.shape1.proxy, contact.shape2.proxy))
          {
            //proxies not collided, remove contact
            var edge1:ContactEdge = contact.edge1;
            var edge2:ContactEdge = contact.edge2;
            var body1:Body = contact.shape1.parent;
            var body2:Body = contact.shape2.parent;
            
            body1.contactEdges.remove(edge1);
            body2.contactEdges.remove(edge2);
            
            Contact.pool.put(contact.dispose());
            ContactEdge.pool.put(edge1.dispose());
            ContactEdge.pool.put(edge2.dispose());
            
            contactIter.remove();
            continue;
          }
        }
        
        contactIter.next();
      }
    }
  }
}